package com.socket1;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Console;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class Server {

    public static void main(String[] args) throws IOException {
        // 开始监听端口
        ServerSocket serverSocket = new ServerSocket(55511);
        while (true){
            try {
                Console.log("server is lister in port:{}", serverSocket.getLocalPort());
                // 该方法会阻塞, 知道有客户端来访问服务端, 然后该方法返回一个socket套接字
                // 如果像本demo一样, 对socket做同步处理, 那做此期间, 服务端不会响应其他客户端, 也就是服务端只能一对一处理.
                // 所以正确对方式是对此处对套接字做异步处理, 但本demo就不增加复杂度了.
                Socket socket = serverSocket.accept();
                String msg = null;
                // 获取socket的输入输出流
                InputStream in = socket.getInputStream();
                DataInputStream dataInputStream = new DataInputStream(in);
                OutputStream out = socket.getOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(out);

                int length;
                // 根据协议, 从客户端获取的第一个字节为数据头, 也就是后续数据体的长度
                while((length = dataInputStream.read()) != -1){
                    // 新建一个字节数组, 长度就是为上面读到的预定长度
                    byte[] bytes = new byte[length];
                    // 直接将数据读取进bytes数组
                    dataInputStream.read(bytes);
                    // 根据协议,已知传递的内容是通过utf8解码的字节, 那服务端就直接将字节流转换为utf8字符串
                    String s = new String(bytes, StandardCharsets.UTF_8);
                    System.out.println("read msg:"+s);
                    // 服务端成功接受消息后, 向客户端发送确认消息的时候, 也按照协议, 先写一个头,再写数据体
                    String ack = "ack";
                    byte[] bytes1 = ack.getBytes(StandardCharsets.UTF_8);
                    ByteBuffer buffer = ByteBuffer.allocate(bytes1.length + 1);
                    buffer.put((byte) bytes1.length);
                    buffer.put(bytes1);
                    dataOutputStream.write(buffer.array());
                    // flush是关键的, 对端才会停止读取
                    dataOutputStream.flush();
                }
                socket.shutdownInput();
                socket.shutdownOutput();
                IoUtil.close(socket);
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }

}
